import os, sys, platform, smtplib, StringIO, MimeWriter, base64, socket, optparse, mimetypes

class CurtusMail(object):
    def __init__(self,sender,reciever,subject,message,attachements,server,printvars):
        self.pref = 'preference'
        self.s_at = '@'
        self.s_is = '='
        self.s_cm = ','
        self.m_sub = 'Subject'
        self.m_ver = 'MIME-Version'
        self.m_vnr = '1.0'
        self.m_tos = 'To'
        self.m_frm = 'From'
        self.m_mod = 'mixed'
        default_en = sys.getdefaultencoding()
        self.m_typ = 'text/plain;charset="%s"' % default_en
        self.a_con = 'Content-Transfer-Encoding'
        self.a_bas = 'base64'
        self.empt = ''
        self.nslu = 'nslookup'
        self.mxop = '-q=MX'
        self.nslookup_error = 'nslookup binary not found in systempath' 
        # Setting platform depending variables
        self.platform_depending()
        # Testing if nslookup is available 
        self.test_nslookup()
        
        # Filling init variables            
        if sender == None:
            self.sender=self.username+self.s_at+self.localhost
        else:
            self.sender = sender
        
        if reciever == None:
            self.reciever = [self.sender]
        else:
            self.reciever = reciever
            
        if subject == None:
            self.subject = ""
        else:
            self.subject = self.get_text(subject)
        
        if message == None:
            self.message = ""
        else:
            self.message = self.get_text(message)

        if attachements == None:
            self.attachements = []
        else:
            self.attachements = attachements
        
        self.server = server
        
        self.build_content()

        self.smtpdic = dict()
        
        self.senderdict = self.address_domain_dic()
            
        if printvars == True:
            self.print_variables()
        else:
            self.send()
        
    def platform_depending(self):
        # If platform differs change these
        uname = platform.uname()
        self.localhost = uname[1]
        if uname[0] == 'Windows':
            self.username = os.environ['USERNAME']
            self.paths = os.environ['PATH'].split(';')
            self.nslu = self.nslu+'.exe'
        else:
            self.username = os.environ['USER']
            self.paths = os.environ['PATH'].split(':')
        #
        self.rejected_data = list()
        self.cmd = ' '.join([self.nslu,self.mxop,self.empt])
    
    def test_nslookup(self):
        nslookup = False
        for path in self.paths:
            testpath = path+os.sep+self.nslu
            if os.path.exists(testpath):
                nslookup = True
        
        if nslookup:
            return(True)
        else:
            self.print_variables()
            raise(EnvironmentError(self.nslookup_error))
            
    def return_domain_from_address(self,email_address):
        return(email_address.split(self.s_at)[1])

    def return_domain_mx(self,domain):
        if self.server != self.localhost:
            servers = list()
            cmd = self.cmd+domain
            execute = os.popen(cmd)
            result = execute.readlines()
            for line in result:
                line = line.lower()
                if self.pref in line:
                    line_list = line.split(self.s_is)
                    prio = int(line_list[1].split(self.s_cm)[0])
                    srvr = line_list[2].strip()
                    servers.append([prio,srvr])
            
            servers.sort()
            if len(servers) == 0:
                servers = [domain]
        else:
            servers = [[0,self.localhost],]
            
        return(servers)
    
    def mx_by_email_address(self,email_address):
        domain = self.return_domain_from_address(email_address)
        mx = self.return_domain_mx(domain)
        return(mx)
    
    def smtp(self,domain):
        msl = self.return_domain_mx(domain)
        connection = [None]
                
        for server in msl:
            server = server[1]
            try:
                tmp = smtplib.SMTP(server)
                connection[0] = tmp
                break
            except:
                if msl[-1][1] == server:
                    r_str1 = '%s domain; ' % domain
                    r_str2 = 'no server accepting connection; '
                    r_str3 = 'tried: %s' % str(msl)
                    error_string = r_str1+r_str2+r_str3
                    self.print_variables()
                    raise(NameError(error_string))
        
        
        return_object = connection[0]
        return(return_object)
    
    def helo(self,smtp,domain):
        code = smtp.helo(smtp.local_hostname)[0]
        if code not in (250,251):
            self.print_variables()
            raise(smtplib.SMTPHeloError(code,domain))
     
    def mail_from(self,smtp,domain):
        code = smtp.mail(self.sender)[0]
        if code != 250:
            self.print_variables()
            raise(smtplib.SMTPSenderRefused(code,domain,self.sender))
                  
    def rcpt_to(self,smtp,domain,address):
        code = smtp.rcpt(address)[0]
        if code != 250:
            self.print_variables()
            raise(smtplip.SMTPRecipientsRefused(address+self.s_cm+domain))
        
    def data(self,smtp,address,message):
        code = smtp.data(message)[0]
        if code != 250:
            self.rejected_data.append([address,code])
            
    def address_domain_dic(self):
        tmp = dict()
        for address in self.reciever:
            domain = self.return_domain_from_address(address)
            try:
                tmp[domain].append(address)
            except:
                tmp[domain] = [address]
        
        return(tmp)
        
    def text_in_file(self,option):
        file_open = open(option,'r')
        file_read = file_open.readlines()
        file_open.close()
        text = "".join(file_read)
        return(text)
    
    def get_text(self,option):
        try:
            return(self.text_in_file(option))
        except:
            return(option)
    
    def get_file_name(self,filename):
        return(os.path.basename(filename))
    
    def build_content(self):
        # set up stream writer    
        self.content = StringIO.StringIO()
        writer = MimeWriter.MimeWriter(self.content)
        # Set the mime headers
        writer.addheader(self.m_sub, self.subject)
        writer.addheader(self.m_ver, self.m_vnr)
        writer.addheader(self.m_frm, self.sender)
        mime_to = ",\n".join(self.reciever)
        writer.addheader(self.m_tos, mime_to)
        # Start multipart 
        writer.startmultipartbody(self.m_mod)
        
        # Put in the body text
        part = writer.nextpart()
        body = part.startbody(self.m_typ)
        body.write(self.message)
        
        # For every attachement add it
        for attachement in self.attachements:
            part = writer.nextpart()
            part.addheader(self.a_con, self.a_bas)
            
            mtype = mimetypes.guess_type(attachement)[0]
            mname = ';name="%s"' % self.get_file_name(attachement)
            content_type_name = mtype+mname             
            
            body = part.startbody(content_type_name)
            base64.encode(open(attachement, 'r'), body)
        
        # Finish multipart   
        writer.lastpart()
        
        
    def send(self):
        if self.server == None:
            # If there is no relay gateway, send mail direct
            # to the reciptients mail servers            
            for domain in self.senderdict:
                self.smtpdic[domain] = self.smtp(domain)
            
            for domain in self.smtpdic:
                addresses = self.senderdict[domain]
                smtp = self.smtpdic[domain]
                self.helo(smtp, domain)
                self.mail_from(smtp, domain)
                for address in addresses:
                    self.rcpt_to(smtp, domain, address)
                    
            for domain in self.smtpdic:
                smtp = self.smtpdic[domain]
                self.data(smtp, address, self.content.getvalue())
                
            if len(self.rejected_data) != 0:
                sys.stderr.write(str(self.rejected_data))
                sys.stdout.write(str(self.rejected_data))
                self.print_variables()
                raise(smtplib.SMTPDataError)
        else:
            # Relay server defined
            relay = smtplib.SMTP(self.server)
            relay.sendmail(self.sender,
                           self.reciever,
                           self.content.getvalue())
            
    def print_variables(self):
        print("Sender:       " +str(self.sender))
        print("Reciever:     " +str(self.reciever))
        print("Subject:      " +str(self.subject))
        print("Message:      " +str(self.message))
        print("Attachements: " +str(self.attachements))
        print("Server:       " +str(self.server))
        print("Mail content:\n" +str(self.content.getvalue()))
        print("\nUse -h flag for description of parameters\n\n")
        
if __name__ == '__main__':
    parser = optparse.OptionParser()
    parser.add_option("-f", 
                      dest="sender", 
                      help="from sender address")
    
    parser.add_option("-t", 
                      dest="recievers", 
                      help="to destination address(s)", 
                      action="append")
    
    parser.add_option("-s", 
                      dest="subject", 
                      help="subject of the mail")
    
    parser.add_option("-m", 
                      dest="message", 
                      help="the message of the mail")
    
    parser.add_option("-a", 
                      dest="attachements", 
                      help="attachement(s) to send", 
                      action="append")
    
    parser.add_option("-r", 
                      dest="server", 
                      help="smtp relay server to use (optional)")
    
    parser.add_option("-v",
                      dest="printvars",
                      help="print the variables as parsed",
                      action="store_true")
    
    (opt, args) = parser.parse_args()
    sender = opt.sender
    reciever = opt.recievers
    subject = opt.subject
    message = opt.message
    attachements = opt.attachements
    server = opt.server
    printvars = opt.printvars
    CurtusMail(sender,reciever,subject,message,attachements,server,printvars)